home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pine / status.c < prev    next >
C/C++ Source or Header  |  1997-02-24  |  36KB  |  1,296 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: status.c,v 4.88 1996/05/14 20:00:17 mikes Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    Pine is in part based on The Elm Mail System:
  32.     ***********************************************************************
  33.     *  The Elm Mail System  -  Revision: 2.13                             *
  34.     *                                                                     *
  35.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  36.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  37.     ***********************************************************************
  38.  
  39.  
  40.   ----------------------------------------------------------------------*/
  41.  
  42. /*======================================================================
  43.      status.c
  44.      Functions that manage the status line (third from the bottom)
  45.        - put messages on the queue to be displayed
  46.        - display messages on the queue with timers 
  47.        - check queue to figure out next timeout
  48.        - prompt for yes/no type of questions
  49.   ====*/
  50.  
  51. #include "headers.h"
  52.  
  53. /*
  54.  * Internal queue of messages.  The circular, double-linked list's 
  55.  * allocated on demand, and cleared as each message is displayed.
  56.  */
  57. typedef struct message {
  58.     char        text[MAX_SCREEN_COLS+1];
  59.     unsigned        flags:8;
  60.     unsigned        shown:1;
  61.     int            min_display_time, max_display_time;
  62.     struct message *next, *prev;
  63. } SMQ_T;
  64.  
  65. #define    LAST_MESSAGE(X)    ((X) == (X)->next)
  66. #define    RAD_BUT_COL    0
  67.  
  68.  
  69. /*
  70.  * Internal prototypes
  71.  */
  72. int  output_message PROTO((SMQ_T *));
  73. void radio_help PROTO((int, int, HelpType));
  74. void draw_radio_prompt PROTO((int, int, char *));
  75. void pause_for_current_message PROTO(());
  76. int  messages_in_queue PROTO(());
  77. void delay_cmd_cue PROTO((int));
  78.  
  79.  
  80.  
  81. /*----------------------------------------------------------------------
  82.      Manage the second line from the bottom where status and error messages
  83. are displayed. A small queue is set up and messages are put on the queue
  84. by calling one of the q_status_message routines. Even though this is a queue
  85. most of the time message will go right on through. The messages are 
  86. displayed just before the read for the next command, or when a read times
  87. out. Read timeouts occur every minute or so for new mail checking and every
  88. few seconds when there are still messages on the queue. Hopefully, this scheme 
  89. will not let messages fly past that the user can't see.
  90.   ----------------------------------------------------------------------*/
  91.  
  92.  
  93. static SMQ_T *message_queue = NULL;
  94. static short  needs_clearing = 0, /* Flag set by want_to()
  95.                                               and optionally_enter() */
  96.           prevstartcol;
  97. static char   prevstatusbuff[MAX_SCREEN_COLS+1];
  98. static time_t displayed_time;
  99.  
  100.  
  101. /*----------------------------------------------------------------------
  102.         Put a message for the status line on the queue
  103.  
  104.   Args: time    -- the min time in seconds to display the message
  105.         message -- message string
  106.  
  107.   Result: queues message on queue represented by static variables
  108.  
  109.     This puts a single message on the queue to be shown.
  110.   ----------*/
  111. void
  112. q_status_message(flags, min_time, max_time, message)
  113.     int   flags;
  114.     int   min_time,max_time;
  115.     char *message;
  116. {
  117.     SMQ_T *new;
  118.  
  119.     if((flags & SM_INFO) && message_queue)
  120.       return;
  121.  
  122.     new = (SMQ_T *)fs_get(sizeof(SMQ_T));
  123.     memset(new, 0, sizeof(SMQ_T));
  124.     strncpy(new->text, message, MAX_SCREEN_COLS);
  125.     new->min_display_time = min_time;
  126.     new->max_display_time = max_time;
  127.     new->flags            = flags;
  128.     if(message_queue){
  129.     new->next = message_queue;
  130.     new->prev = message_queue->prev;
  131.     new->prev->next = message_queue->prev = new;
  132.     }
  133.     else
  134.       message_queue = new->next = new->prev = new;
  135.  
  136.     dprint(9, (debugfile, "q_status_message(%.40s)\n", message));
  137. }
  138.  
  139.  
  140. /*----------------------------------------------------------------------
  141.         Put a message with 1 printf argument on queue for status line
  142.  
  143.     Args: min_t -- minimum time to display message for
  144.           max_t -- minimum time to display message for
  145.           s -- printf style control string
  146.           a -- argument for printf
  147.  
  148.    Result: message queued
  149.   ----*/
  150.  
  151. /*VARARGS1*/
  152. void
  153. q_status_message1(flags, min_t, max_t, s, a)
  154.     int      flags;
  155.     int   min_t, max_t;
  156.     char *s;
  157.     void *a;
  158. {
  159.     char buf[1000];
  160.  
  161.     sprintf(buf, s, a);
  162.     q_status_message(flags, min_t, max_t, buf);
  163. }
  164.  
  165.  
  166.  
  167. /*----------------------------------------------------------------------
  168.         Put a message with 2 printf argument on queue for status line
  169.  
  170.     Args: min_t  -- minimum time to display message for
  171.           max_t  -- maximum time to display message for
  172.           s  -- printf style control string
  173.           a1 -- argument for printf
  174.           a2 -- argument for printf
  175.  
  176.   Result: message queued
  177.   ---*/
  178.  
  179. /*VARARGS1*/
  180. void
  181. q_status_message2(flags, min_t, max_t, s, a1, a2)
  182.     int   flags;
  183.     int   min_t, max_t;
  184.     char *s;
  185.     void *a1, *a2;
  186. {
  187.     char buf[1000];
  188.  
  189.     sprintf(buf, s, a1, a2);
  190.     q_status_message(flags, min_t, max_t, buf);
  191. }
  192.  
  193.  
  194.  
  195. /*----------------------------------------------------------------------
  196.         Put a message with 3 printf argument on queue for status line
  197.  
  198.     Args: min_t  -- minimum time to display message for
  199.           max_t  -- maximum time to display message for
  200.           s  -- printf style control string
  201.           a1 -- argument for printf
  202.           a2 -- argument for printf
  203.           a3 -- argument for printf
  204.  
  205.   Result: message queued
  206.   ---*/
  207.  
  208. /*VARARGS1*/
  209. void
  210. q_status_message3(flags, min_t, max_t, s, a1, a2, a3)
  211.     int   flags;
  212.     int   min_t, max_t;
  213.     char *s;
  214.     void *a1, *a2, *a3;
  215. {
  216.     char buf[1000];
  217.  
  218.     sprintf(buf, s, a1, a2, a3);
  219.     q_status_message(flags, min_t, max_t, buf);
  220. }
  221.  
  222.  
  223.  
  224. /*----------------------------------------------------------------------
  225.         Put a message with 4 printf argument on queue for status line
  226.  
  227.  
  228.     Args: min_t  -- minimum time to display message for
  229.           max_t  -- maximum time to display message for
  230.           s  -- printf style control string
  231.           a1 -- argument for printf
  232.           a2 -- argument for printf
  233.           a3 -- argument for printf
  234.           a4 -- argument for printf
  235.  
  236.   Result: message queued
  237.   ----------------------------------------------------------------------*/
  238. /*VARARGS1*/
  239. void
  240. q_status_message4(flags, min_t, max_t, s, a1, a2, a3, a4)
  241.     int   flags;
  242.     int   min_t, max_t;
  243.     char *s;
  244.     void *a1, *a2, *a3, *a4;
  245. {
  246.     char buf[1000];
  247.  
  248.     sprintf(buf, s, a1, a2, a3, a4);
  249.     q_status_message(flags, min_t, max_t, buf);
  250. }
  251.  
  252.  
  253. /*----------------------------------------------------------------------
  254.         Put a message with 7 printf argument on queue for status line
  255.  
  256.  
  257.     Args: min_t  -- minimum time to display message for
  258.           max_t  -- maximum time to display message for
  259.           s  -- printf style control string
  260.           a1 -- argument for printf
  261.           a2 -- argument for printf
  262.           a3 -- argument for printf
  263.           a4 -- argument for printf
  264.           a5 -- argument for printf
  265.           a6 -- argument for printf
  266.           a7 -- argument for printf
  267.  
  268.  
  269.   Result: message queued
  270.   ----------------------------------------------------------------------*/
  271. /*VARARGS1*/
  272. void
  273. q_status_message7(flags, min_t, max_t, s, a1, a2, a3, a4, a5, a6, a7)
  274.     int   flags;
  275.     int   min_t, max_t;
  276.     char *s;
  277.     void *a1, *a2, *a3, *a4, *a5, *a6, *a7;
  278. {
  279.     char buf[1000];
  280.  
  281.     sprintf(buf, s, a1, a2, a3, a4, a5, a6, a7);
  282.     q_status_message(flags, min_t, max_t, buf);
  283. }
  284.  
  285.  
  286. /*VARARGS1*/
  287. void
  288. q_status_message8(flags, min_t, max_t, s, a1, a2, a3, a4, a5, a6, a7, a8)
  289.     int   flags;
  290.     int   min_t, max_t;
  291.     char *s;
  292.     void *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
  293. {
  294.     char buf[1000];
  295.  
  296.     sprintf(buf, s, a1, a2, a3, a4, a5, a6, a7, a8);
  297.     q_status_message(flags, min_t, max_t, buf);
  298. }
  299.  
  300.  
  301. /*----------------------------------------------------------------------
  302.      Mark the status line as dirty so it gets cleared next chance
  303.  ----*/
  304. void
  305. mark_status_dirty()
  306. {
  307.     mark_status_unknown();
  308.     needs_clearing++;
  309. }
  310.  
  311.  
  312. /*----------------------------------------------------------------------
  313.     Cause status line drawing optimization to be turned off, because we
  314.     don't know what the status line looks like.
  315.  ----*/
  316. void
  317. mark_status_unknown()
  318. {
  319.     prevstartcol = -1;
  320.     prevstatusbuff[0]  = '\0';
  321. }
  322.  
  323.  
  324.  
  325. /*----------------------------------------------------------------------
  326.      Wait a suitable amount of time for the currently displayed message
  327.  ----*/
  328. void
  329. pause_for_current_message()
  330. {
  331.     if(message_queue){
  332.     int w;
  333.  
  334.     if(w = status_message_remaining()){
  335.         delay_cmd_cue(1);
  336.         sleep(w);
  337.         delay_cmd_cue(0);
  338.     }
  339.  
  340.     d_q_status_message();
  341.     }
  342. }
  343.  
  344.  
  345. /*----------------------------------------------------------------------
  346.     Time remaining for current message's minimum display
  347.  ----*/
  348. int
  349. status_message_remaining()
  350. {
  351.     if(message_queue){
  352.     int d = (int)(displayed_time - time(0))
  353.                       + message_queue->min_display_time;
  354.     return((d > 0) ? d : 0);
  355.     }
  356.  
  357.     return(0);
  358. }
  359.  
  360.  
  361. /*----------------------------------------------------------------------
  362.         Find out how many messages are queued for display
  363.  
  364.   Args:   dtime -- will get set to minimum display time for current message
  365.  
  366.   Result: number of messages in the queue.
  367.  
  368.   ---------*/
  369. int
  370. messages_queued(dtime)
  371.     long *dtime;
  372. {
  373.     if(message_queue && dtime)
  374.       *dtime = (long)max(message_queue->min_display_time, 1L);
  375.  
  376.     return((ps_global->in_init_seq) ? 0 : messages_in_queue());
  377. }
  378.  
  379.  
  380.  
  381. /*----------------------------------------------------------------------
  382.        Return number of messages in queue
  383.   ---------*/
  384. int
  385. messages_in_queue()
  386. {
  387.     int       n = message_queue ? 1 : 0;
  388.     SMQ_T *p = message_queue;
  389.  
  390.     while(n && (p = p->next) != message_queue)
  391.       n++;
  392.  
  393.     return(n);
  394. }
  395.  
  396.  
  397.  
  398. /*----------------------------------------------------------------------
  399.      Return last message queued
  400.   ---------*/
  401. char *
  402. last_message_queued()
  403. {
  404.     SMQ_T *p, *r = NULL;
  405.  
  406.     if(p = message_queue){
  407.     do
  408.       if(p->flags & SM_ORDER)
  409.         r = p;
  410.     while((p = p->next) != message_queue);
  411.     }
  412.  
  413.     return(r ? r->text : NULL);
  414. }
  415.  
  416.  
  417.  
  418. /*----------------------------------------------------------------------
  419.        Update status line, clearing or displaying a message
  420.  
  421.    Arg: command -- The command that is about to be executed
  422.  
  423.   Result: status line cleared or
  424.              next message queued is displayed or
  425.              current message is redisplayed.
  426.          if next message displayed, it's min display time
  427.          is returned else if message already displayed, it's
  428.          time remaining on the display is returned, else 0.
  429.  
  430.    This is called when ready to display the next message, usually just
  431. before reading the next command from the user. We pass in the nature
  432. of the command because it affects what we do here. If the command just
  433. executed by the user is a redraw screen, we don't want to reset or go to 
  434. next message because it might not have been seen.  Also if the command
  435. is just a noop, which are usually executed when checking for new mail 
  436. and happen every few minutes, we don't clear the message.
  437.  
  438. If it was really a command and there's nothing more to show, then we
  439. clear, because we know the user has seen the message. In this case the
  440. user might be typing commands very quickly and miss a message, so
  441. there is a time stamp and time check that each message has been on the
  442. screen for a few seconds.  If it hasn't we just return and let it be
  443. taken care of next time.
  444.  
  445. At slow terminal output speeds all of this can be for naught, the amount
  446. of time it takes to paint the screen when the whole screen is being painted
  447. is greater than the second or two delay so the time stamps set here have
  448. nothing to do with when the user actually sees the message.
  449. ----------------------------------------------------------------------*/
  450. int
  451. display_message(command)
  452.     int command;
  453. {
  454.     if(ps_global == NULL || ps_global->ttyo == NULL
  455.        || ps_global->ttyo->screen_rows <= 1 || ps_global->in_init_seq)
  456.       return(0);
  457.  
  458.     /*---- Deal with any previously displayed messages ----*/
  459.     if(message_queue && message_queue->shown) {
  460.     int rv = -1;
  461.  
  462.     if(command == ctrl('L')) {    /* just repaint it, and go on */
  463.         mark_status_unknown();
  464.         mark_keymenu_dirty();
  465.         mark_titlebar_dirty();
  466.         rv = 0;
  467.     }
  468.     else {                /* ensure sufficient time's passed */
  469.         time_t now;
  470.         int    diff;
  471.  
  472.         now  = time(0);
  473.         diff = (int)(displayed_time - now)
  474.             + ((command == NO_OP_COMMAND || command == NO_OP_IDLE)
  475.                 ? message_queue->max_display_time
  476.                 : message_queue->min_display_time);
  477.             dprint(9, (debugfile,
  478.                "STATUS: diff:%d, displayed: %ld, now: %ld\n",
  479.                diff, displayed_time, now));
  480.             if(diff > 0)
  481.           rv = diff;            /* check again next time  */
  482.         else if(LAST_MESSAGE(message_queue)
  483.             && (command == NO_OP_COMMAND || command == NO_OP_IDLE)
  484.             && message_queue->max_display_time)
  485.           rv = 0;                /* last msg, no cmd, has max */
  486.     }
  487.  
  488.     if(rv >= 0){                /* leave message displayed? */
  489.         if(prevstartcol < 0)        /* need to redisplay it? */
  490.           output_message(message_queue);
  491.  
  492.         return(rv);
  493.     }
  494.       
  495.     d_q_status_message();            /* remove it from queue and */
  496.     needs_clearing++;            /* clear the line if needed */
  497.     }
  498.  
  499.     if(!message_queue && (command == ctrl('L') || needs_clearing)) {
  500.     dprint(9, (debugfile, "Clearing status line\n"));
  501.     ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
  502.     mark_status_unknown();
  503.     if(command == ctrl('L')){
  504.         mark_keymenu_dirty();
  505.         mark_titlebar_dirty();
  506.     }
  507.     }
  508.  
  509.     /*---- Display any queued messages, weeding 0 disp times ----*/
  510.     while(message_queue && !message_queue->shown)
  511.       if(message_queue->min_display_time || LAST_MESSAGE(message_queue)){
  512.       displayed_time = time(0);
  513.       output_message(message_queue);
  514.       }
  515.       else
  516.     d_q_status_message();
  517.  
  518.     needs_clearing = 0;                /* always cleared or written */
  519.     dprint(9, (debugfile,
  520.                "STATUS cmd:%d, max:%d, min%d\n", command, 
  521.            (message_queue) ? message_queue->max_display_time : -1,
  522.            (message_queue) ? message_queue->min_display_time : -1));
  523.     fflush(stdout);
  524.     return(0);
  525. }
  526.  
  527.  
  528.  
  529. /*----------------------------------------------------------------------
  530.      Display all the messages on the queue as quickly as possible
  531.   ----*/
  532. void
  533. flush_status_messages(skip_last_pause)
  534.     int skip_last_pause;
  535. {
  536.     while(message_queue){
  537.     if(LAST_MESSAGE(message_queue)
  538.        && skip_last_pause
  539.        && message_queue->shown)
  540.       break;
  541.  
  542.     if(message_queue->shown)
  543.       pause_for_current_message();
  544.  
  545.     while(message_queue && !message_queue->shown)
  546.       if(message_queue->min_display_time
  547.          || LAST_MESSAGE(message_queue)){
  548.           displayed_time = time(0);
  549.           output_message(message_queue);
  550.       }
  551.       else
  552.         d_q_status_message();
  553.     }
  554. }
  555.  
  556.  
  557.  
  558. /*----------------------------------------------------------------------
  559.      Make sure any and all SM_ORDER messages get displayed.
  560.  
  561.      Note: This flags the message line as having nothing displayed.
  562.            The idea is that it's a function called by routines that want
  563.        the message line for a prompt or something, and that they're
  564.        going to obliterate the message anyway.
  565.  ----*/
  566. void
  567. flush_ordered_messages()
  568. {
  569.     SMQ_T *start = NULL;
  570.  
  571.     while(message_queue && message_queue != start){
  572.     if(message_queue->shown)
  573.       pause_for_current_message(); /* changes "message_queue" */
  574.  
  575.     while(message_queue && !message_queue->shown
  576.           && message_queue != start)
  577.       if(message_queue->flags & SM_ORDER){
  578.           if(message_queue->min_display_time){
  579.           displayed_time = time(0);
  580.           output_message(message_queue);
  581.           }
  582.           else
  583.         d_q_status_message();
  584.       }
  585.       else if(!start)
  586.         start = message_queue;
  587.     }
  588. }
  589.  
  590.  
  591.      
  592. /*----------------------------------------------------------------------
  593.       Remove a message from the message queue.
  594.   ----*/
  595. void
  596. d_q_status_message()
  597. {
  598.     if(message_queue){
  599.     dprint(9, (debugfile, "d_q_status_message(%.40s)\n",
  600.            message_queue->text));
  601.     if(!LAST_MESSAGE(message_queue)){
  602.         SMQ_T *p = message_queue;
  603.         p->next->prev = p->prev;
  604.         message_queue = p->prev->next = p->next;
  605.         fs_give((void **)&p);
  606.     }
  607.     else
  608.       fs_give((void **)&message_queue);
  609.     }
  610. }
  611.  
  612.  
  613.  
  614. /*----------------------------------------------------------------------
  615.     Actually output the message to the screen
  616.  
  617.   Args: message            -- The message to output
  618.     from_alarm_handler -- Called from alarm signal handler.
  619.                   We don't want to add this message to the review
  620.                   message list since we may mess with the malloc
  621.                   arena here, and the interrupt may be from
  622.                   the middle of something malloc'ing.
  623.  ----*/
  624. int
  625. status_message_write(message, from_alarm_handler)
  626.     char *message;
  627.     int   from_alarm_handler;
  628. {
  629.     int  col, row, max_length, invert;
  630.     char obuff[MAX_SCREEN_COLS + 1];
  631.  
  632.     if(!from_alarm_handler)
  633.       add_review_message(message);
  634.  
  635.     invert = !InverseState();    /* already in inverse? */
  636.     row = max(0, ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
  637.  
  638.     /* Put [] around message and truncate to screen width */
  639.     max_length = ps_global->ttyo != NULL ? ps_global->ttyo->screen_cols : 80;
  640.     max_length = min(max_length, MAX_SCREEN_COLS);
  641.     obuff[0] = '[';
  642.     obuff[1] = '\0';
  643.     strncat(obuff, message, max_length - 2);
  644.     obuff[max_length - 1] = '\0';
  645.     strcat(obuff, "]");
  646.  
  647.     if(prevstartcol == -1 || strcmp(obuff, prevstatusbuff)){
  648.     /*
  649.      * Try to optimize drawing in this case.  If the length of the string
  650.      * changed, it is very likely a lot different, so probably not
  651.      * worth looking at.  Just go down the two strings drawing the
  652.      * characters that have changed.
  653.      */
  654.     if(prevstartcol != -1 && strlen(obuff) == strlen(prevstatusbuff)){
  655.         char *p, *q, *uneq_str;
  656.         int   column, start_col;
  657.  
  658.         if(invert)
  659.           StartInverse();
  660.  
  661.         q = prevstatusbuff;
  662.         p = obuff;
  663.         col = column = prevstartcol;
  664.  
  665.         while(*q){
  666.         /* skip over string of equal characters */
  667.         while(*q && *p == *q){
  668.             q++;
  669.             p++;
  670.             column++;
  671.         }
  672.  
  673.         if(!*q)
  674.           break;
  675.  
  676.         uneq_str  = p;
  677.         start_col = column;
  678.  
  679.         /* find end of string of unequal characters */
  680.         while(*q && *p != *q){
  681.             *q++ = *p++;  /* update prevstatusbuff */
  682.             column++;
  683.         }
  684.  
  685.         /* tie off and draw the changed chars */
  686.         *p = '\0';
  687.         PutLine0(row, start_col, uneq_str);
  688.  
  689.         if(*q){
  690.             p++;
  691.             q++;
  692.             column++;
  693.         }
  694.         }
  695.  
  696.         if(invert)
  697.           EndInverse();
  698.  
  699.         /* move cursor to a consistent position */
  700.         MoveCursor(row, 0);
  701.         fflush(stdout);
  702.     }
  703.     else{
  704.         ClearLine(row);
  705.         if(invert)
  706.           StartInverse();
  707.  
  708.         col = Centerline(row, obuff);
  709.         if(invert)
  710.           EndInverse();
  711.  
  712.         MoveCursor(row, 0);
  713.         fflush(stdout);
  714.         strcpy(prevstatusbuff, obuff);
  715.         prevstartcol = col;
  716.     }
  717.     }
  718.     else
  719.       col = prevstartcol;
  720.  
  721.     return(col);
  722. }
  723.  
  724.  
  725.  
  726. /*----------------------------------------------------------------------
  727.     Write the given status message to the display.
  728.  
  729.   Args: mq_entry -- pointer to message queue entry to write.
  730.  
  731.  ----*/
  732. int 
  733. output_message(mq_entry)
  734.     SMQ_T *mq_entry;
  735. {
  736.     int rv;
  737.  
  738.     dprint(9, (debugfile, "output_message(%s)\n", mq_entry->text));
  739.  
  740.     if((mq_entry->flags & SM_DING) && F_OFF(F_QUELL_BEEPS, ps_global))
  741.       Writechar(BELL, 0);            /* ring bell */
  742.       /* flush() handled below */
  743.  
  744.     rv = status_message_write(mq_entry->text, 0);
  745.  
  746.     /*
  747.      * If we're a modal message, loop waiting for acknowlegment
  748.      * *and* keep the stream alive.
  749.      */
  750.     if((mq_entry->flags & SM_MODAL) && !mq_entry->shown){
  751.     static char *modal_msg =
  752.          "* * * Read message above then hit Return to continue Pine * * *";
  753.     int ch;
  754.  
  755.     ClearLine(ps_global->ttyo->screen_rows-2);
  756.     MoveCursor(ps_global->ttyo->screen_rows-2,
  757.            max((ps_global->ttyo->screen_rows-sizeof(modal_msg))/2, 0));
  758.     Write_to_screen(modal_msg);
  759.     ClearLine(ps_global->ttyo->screen_rows-1);
  760.     mark_status_dirty();
  761.     mark_keymenu_dirty();
  762.  
  763.     while(1){
  764.         flush_input();
  765.         MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global), 0);
  766.         if((ch = read_char(600)) == NO_OP_COMMAND || ch == NO_OP_IDLE)
  767.           new_mail(0, 2, 0);        /* wake up and prod server */
  768.         else if(ch != ctrl('M'))
  769.           Writechar(BELL, 0);        /* complain */
  770.         else
  771.           break;                /* done. */
  772.     }
  773.  
  774.     if(FOOTER_ROWS(ps_global) != 1){
  775.         ClearLine(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global));
  776.         if(!ps_global->mangled_footer)
  777.           redraw_keymenu();            /* else repaint next pass... */
  778.     }
  779.     else if(ps_global->redrawer)
  780.       (*ps_global->redrawer)();
  781.     }
  782.     else if(!(mq_entry->flags & SM_MODAL) && ps_global->status_msg_delay){
  783.     MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global), 0);
  784.     fflush(stdout);
  785.     sleep(ps_global->status_msg_delay);
  786.     }
  787.  
  788.     mq_entry->shown = 1;
  789.     return(rv);
  790. }
  791.  
  792.  
  793.  
  794. /*----------------------------------------------------------------------
  795.     Write or clear delay cue
  796.  
  797.   Args: on -- whether to turn it on or not
  798.  
  799.  ----*/
  800. void
  801. delay_cmd_cue(on)
  802.     int on;
  803. {
  804.     int l;
  805.  
  806.     if(prevstartcol >= 0 && (l = strlen(prevstatusbuff))){
  807.     MoveCursor(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global),
  808.            prevstartcol ? max(prevstartcol - 1, 0) : 0);
  809.     StartInverse();
  810.     Write_to_screen(on ? "[>" : " [");
  811.     EndInverse();
  812.     MoveCursor(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global),
  813.            min(prevstartcol + l, ps_global->ttyo->screen_cols) - 1);
  814.     StartInverse();
  815.     Write_to_screen(on ? "<]" : "] ");
  816.     EndInverse();
  817.     MoveCursor(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global), 0);
  818.     }
  819.  
  820.     fflush(stdout);
  821. #ifdef    _WINDOWS
  822.     mswin_setcursor ((on) ? MSWIN_CURSOR_BUSY : MSWIN_CURSOR_ARROW);
  823. #endif
  824. }
  825.  
  826.  
  827.  
  828. /*
  829.  * want_to's array passed to radio_buttions...
  830.  */
  831. static ESCKEY_S yorn[] = {
  832.     {'y', 'y', "Y", "Yes"},
  833.     {'n', 'n', "N", "No"},
  834.     {-1, 0, NULL, NULL}
  835. };
  836.  
  837.  
  838. /*----------------------------------------------------------------------
  839.      Ask a yes/no question in the status line
  840.  
  841.    Args: question     -- string to prompt user with
  842.          dflt         -- The default answer to the question (should probably
  843.              be y or n)
  844.          on_ctrl_C    -- Answer returned on ^C
  845.      help         -- Two line help text
  846.      display_help -- If true, display help without being asked
  847.  
  848.  Result: Messes up the status line,
  849.          returns y, n, dflt, or dflt_C
  850.   ---*/
  851. int
  852. want_to(question, dflt, on_ctrl_C, help, display_help, flush)
  853.     char    *question;
  854.     HelpType    help;
  855.     int        dflt, on_ctrl_C, display_help, flush;
  856. {
  857.     char *q2;
  858.     int      rv;
  859.  
  860. #ifdef _WINDOWS
  861.     if (mswin_usedialog ()) {
  862.     mswin_flush ();
  863.     switch (mswin_yesno (question)) {
  864.     default:
  865.     case 0:        return (on_ctrl_C);
  866.     case 1:        return ('y');
  867.     case 2:        return ('n');
  868.         }
  869.     }
  870. #endif
  871.  
  872.     /*----
  873.        One problem with adding the (y/n) here is that shrinking the 
  874.        screen while in radio_buttons() will cause it to get chopped
  875.        off. It would be better to truncate the question passed in
  876.        hear and leave the full "(y/n) [x] : " on.
  877.       ----*/
  878.     q2 = fs_get(strlen(question) + 6);
  879.     sprintf(q2, "%.*s? ", ps_global->ttyo->screen_cols - 6, question);
  880.     if(on_ctrl_C == 'n')    /* don't ever let cancel == 'n' */
  881.       on_ctrl_C = 0;
  882.  
  883.     rv = radio_buttons(q2,
  884.     (ps_global->ttyo->screen_rows > 4) ? - FOOTER_ROWS(ps_global) : -1,
  885.     yorn, dflt, on_ctrl_C, help, flush ? RB_FLUSH_IN : RB_NORM);
  886.     fs_give((void **)&q2);
  887.  
  888.     return(rv);
  889. }
  890.  
  891.  
  892. int
  893. one_try_want_to(question, dflt, on_ctrl_C, help, display_help, flush)
  894.     char      *question;
  895.     HelpType   help;
  896.     int    dflt, on_ctrl_C, display_help, flush;
  897. {
  898.     char     *q2;
  899.     int          rv;
  900.  
  901.     q2 = fs_get(strlen(question) + 6);
  902.     sprintf(q2, "%.*s? ", ps_global->ttyo->screen_cols - 6, question);
  903.     rv = radio_buttons(q2,
  904.     (ps_global->ttyo->screen_rows > 4) ? - FOOTER_ROWS(ps_global) : -1,
  905.     yorn, dflt, on_ctrl_C, help,
  906.         (flush ? RB_FLUSH_IN : RB_NORM) | RB_ONE_TRY);
  907.     fs_give((void **)&q2);
  908.  
  909.     return(rv);
  910. }
  911.  
  912.  
  913.  
  914. /*----------------------------------------------------------------------
  915.     Prompt user for a choice among alternatives
  916.  
  917. Args --  prompt:    The prompt for the question/selection
  918.          line:      The line to prompt on, if negative then relative to bottom
  919.          esc_list:  ESC_KEY_S list of keys
  920.          dflt:        The selection when the <CR> is pressed (should probably
  921.               be one of the chars in esc_list)
  922.          on_ctrl_C: The selection when ^C is pressed
  923.          help_text: Text to be displayed on bottom two lines
  924.      flags:     Logically OR'd flags modifying our behavior to:
  925.         RB_FLUSH_IN    - Discard any pending input chars.
  926.         RB_ONE_TRY     - Only give one chance to answer.  Returns
  927.                  on_ctrl_C value if not answered acceptably
  928.                  on first try.
  929.         RB_NO_NEWMAIL  - Quell the usual newmail check.
  930.     
  931.      Note: If there are enough keys in the esc_list to need a second
  932.            screen, and there is no help, then the 13th key will be
  933.            put in the help position.
  934.  
  935. Result -- Returns the letter pressed. Will be one of the characters in the
  936.           esc_list argument or one of the two deefaults.
  937.  
  938. This will pause for any new status message to be seen and then prompt the user.
  939. The prompt will be truncated to fit on the screen. Redraw and resize are
  940. handled along with ^Z suspension. Typing ^G will toggle the help text on and
  941. off. Character types that are not buttons will result in a beep (unless one_try
  942. is set).
  943.   ----*/
  944. int
  945. radio_buttons(prompt, line, esc_list, dflt, on_ctrl_C, help_text, flags)
  946.     char     *prompt;
  947.     int          line;
  948.     ESCKEY_S *esc_list;
  949.     int       dflt;
  950.     int       on_ctrl_C;
  951.     HelpType  help_text;
  952.     int          flags;
  953. {
  954.     register int     ch, real_line;
  955.     char            *q, *ds = NULL;
  956.     int              cursor_moved, max_label, i, start, fkey_table[12];
  957.     int             km_popped = 0;
  958.     struct key         rb_keys[12];
  959.     struct key_menu  rb_keymenu;
  960.     bitmap_t         bitmap;
  961.  
  962. #ifdef _WINDOWS
  963.     if (mswin_usedialog ()) {
  964.     MDlgButton        button_list[12];
  965.     int            b;
  966.     int            i;
  967.     int            ret;
  968.     char            **help;
  969.  
  970.     memset (&button_list, 0, sizeof (MDlgButton) * 12);
  971.     b = 0;
  972.     for (i = 0; esc_list && esc_list[i].ch != -1 && i < 11; ++i) {
  973.         button_list[b].ch = esc_list[i].ch;
  974.         button_list[b].rval = esc_list[i].rval;
  975.         button_list[b].name = esc_list[i].name;
  976.         button_list[b].label = esc_list[i].label;
  977.         ++b;
  978.     }
  979.     button_list[b].ch = -1;
  980.     
  981.     help = get_help_text (help_text, NULL);
  982.  
  983.     ret = mswin_select (prompt, button_list, dflt, on_ctrl_C, 
  984.             help, flags);
  985.     if (help != NULL) 
  986.         free_help_text (help);
  987.     return (ret);
  988.     }
  989. #endif
  990.  
  991.     suspend_busy_alarm();
  992.     flush_ordered_messages();        /* show user previous status msgs */
  993.     mark_status_dirty();        /* clear message next display call */
  994.     real_line = line > 0 ? line : ps_global->ttyo->screen_rows + line;
  995.     MoveCursor(real_line, RAD_BUT_COL);
  996.     CleartoEOLN();
  997.  
  998.     /*---- Find longest label ----*/
  999.     max_label = 0;
  1000.     for(i = 0; esc_list && esc_list[i].ch != -1 && i < 12; i++){
  1001.       if(esc_list[i].ch == -2) /* -2 means to skip this key and leave blank */
  1002.     continue;
  1003.       max_label = max(max_label, strlen(esc_list[i].name));
  1004.     }
  1005.  
  1006.     q = cpystr(prompt); /* So we can truncate string below if need be */
  1007.     if(strlen(q) + max_label > ps_global->ttyo->screen_cols) 
  1008.         q[ps_global->ttyo->screen_cols - max_label] = '\0';
  1009.  
  1010.     /*---- Init structs for keymenu ----*/
  1011.     for(i = 0; i < 12; i++)
  1012.       memset((void *)&rb_keys[i], 0, sizeof(struct key));
  1013.  
  1014.     memset((void *)&rb_keymenu, 0, sizeof(struct key_menu));
  1015.     rb_keymenu.how_many = 1;
  1016.     rb_keymenu.keys     = rb_keys;
  1017.  
  1018.     /*---- Setup key menu ----*/
  1019.     start = 0;
  1020.     clrbitmap(bitmap);
  1021.     memset(fkey_table, NO_OP_COMMAND, 12 * sizeof(int));
  1022.     if(help_text != NO_HELP){        /* if shown, always at position 0 */
  1023.     rb_keymenu.keys[0].name  = "?";
  1024.     rb_keymenu.keys[0].label = "Help";
  1025.     setbitn(0, bitmap);
  1026.     fkey_table[0] = ctrl('G');
  1027.     start++;
  1028.     }
  1029.  
  1030.     if(on_ctrl_C){            /* if shown, always at position 1 */
  1031.     rb_keymenu.keys[1].name  = "^C";
  1032.     rb_keymenu.keys[1].label = "Cancel";
  1033.     setbitn(1, bitmap);
  1034.     fkey_table[1] = ctrl('C');
  1035.     start++;
  1036.     }
  1037.  
  1038.     start = (start) ? 2 : 0;
  1039.     /*---- Show the usual possible keys ----*/
  1040.     for(i=start; esc_list && esc_list[i-start].ch != -1 && i <= 12; i++){
  1041.     /*
  1042.      * If we have an esc_list item we'd like to put in the non-existent
  1043.      * 13th slot, and there is no help, we put it in the help slot
  1044.      * instead.  We're hacking now...!
  1045.      */
  1046.     if(i == 12){
  1047.         if(help_text != NO_HELP)
  1048.           panic("Programming botch in radio_buttons(): too many keys");
  1049.  
  1050.         if(esc_list[i-start].ch != -2)
  1051.           setbitn(0, bitmap); /* the help slot */
  1052.  
  1053.         fkey_table[0] = esc_list[i-start].ch;
  1054.         rb_keymenu.keys[0].name  = esc_list[i-start].name;
  1055.         if(esc_list[i-start].rval == dflt){
  1056.         ds = (char *)fs_get((strlen(esc_list[i-start].label) + 3)
  1057.                     * sizeof(char));
  1058.         sprintf(ds, "[%s]", esc_list[i-start].label);
  1059.         rb_keymenu.keys[0].label = ds;
  1060.         }
  1061.         else
  1062.           rb_keymenu.keys[0].label = esc_list[i-start].label;
  1063.     }
  1064.     else{
  1065.         if(esc_list[i-start].ch != -2)
  1066.           setbitn(i, bitmap);
  1067.  
  1068.         fkey_table[i] = esc_list[i-start].ch;
  1069.         rb_keymenu.keys[i].name  = esc_list[i-start].name;
  1070.         if(esc_list[i-start].rval == dflt){
  1071.         ds = (char *)fs_get((strlen(esc_list[i-start].label) + 3)
  1072.                     * sizeof(char));
  1073.         sprintf(ds, "[%s]", esc_list[i-start].label);
  1074.         rb_keymenu.keys[i].label = ds;
  1075.         }
  1076.         else
  1077.           rb_keymenu.keys[i].label = esc_list[i-start].label;
  1078.     }
  1079.     }
  1080.  
  1081.     for(; i < 12; i++)
  1082.       rb_keymenu.keys[i].name = NULL;
  1083.  
  1084.     ps_global->mangled_footer = 1;
  1085.  
  1086.     draw_radio_prompt(real_line, RAD_BUT_COL, q);
  1087.  
  1088.     while(1){
  1089.         fflush(stdout);
  1090.  
  1091.     /*---- Paint the keymenu ----*/
  1092.     EndInverse();
  1093.     draw_keymenu(&rb_keymenu, bitmap, ps_global->ttyo->screen_cols,
  1094.              1 - FOOTER_ROWS(ps_global), 0, FirstMenu, 0);
  1095.     StartInverse();
  1096.     MoveCursor(real_line, RAD_BUT_COL + strlen(q));
  1097.  
  1098.     if(flags & RB_FLUSH_IN)
  1099.       flush_input();
  1100.  
  1101.   newcmd:
  1102.     /* Timeout 5 min to keep imap mail stream alive */
  1103.         ch = read_char(600);
  1104.         dprint(2, (debugfile,
  1105.                    "Want_to read: %s (%d)\n", pretty_command(ch), ch));
  1106.     if(isascii(ch) && isupper((unsigned char)ch))
  1107.       ch = tolower((unsigned char)ch);
  1108.  
  1109.     if(F_ON(F_USE_FK,ps_global)
  1110.        && ((isascii(ch) && isalpha((unsigned char)ch) && !strchr("YyNn",ch))
  1111.            || ((ch >= PF1 && ch <= PF12)
  1112.            && (ch = fkey_table[ch - PF1]) == NO_OP_COMMAND))){
  1113.         /*
  1114.          * The funky test above does two things.  It maps
  1115.          * esc_list character commands to function keys, *and* prevents
  1116.          * character commands from input while in function key mode.
  1117.          * NOTE: this breaks if we ever need more than the first
  1118.          * twelve function keys...
  1119.          */
  1120.         if(flags & RB_ONE_TRY){
  1121.         ch = on_ctrl_C;
  1122.             goto out_of_loop;
  1123.         }
  1124.         Writechar(BELL, 0);
  1125.         continue;
  1126.     }
  1127.  
  1128.         switch(ch) {
  1129.  
  1130.           default:
  1131.         for(i = 0; esc_list && esc_list[i].ch != -1; i++)
  1132.           if(ch == esc_list[i].ch){
  1133.           int len, n;
  1134.  
  1135.           MoveCursor(real_line, len = RAD_BUT_COL + strlen(q));
  1136.           for(n = 0, len = ps_global->ttyo->screen_cols - len;
  1137.               esc_list[i].label[n] && len > 0;
  1138.               n++, len--)
  1139.             Writechar(esc_list[i].label[n], 0);
  1140.  
  1141.           ch = esc_list[i].rval;
  1142.           goto out_of_loop;
  1143.           }
  1144.  
  1145.         if(flags & RB_ONE_TRY){
  1146.         ch = on_ctrl_C;
  1147.             goto out_of_loop;
  1148.         }
  1149.         Writechar(BELL, 0);
  1150.         break;
  1151.  
  1152.           case ctrl('M'):
  1153.           case ctrl('J'):
  1154.             ch = dflt;
  1155.             goto out_of_loop;
  1156.  
  1157.           case ctrl('C'):
  1158.         if(on_ctrl_C || (flags & RB_ONE_TRY)){
  1159.         ch = on_ctrl_C;
  1160.         goto out_of_loop;
  1161.         }
  1162.  
  1163.         Writechar(BELL, 0);
  1164.         break;
  1165.  
  1166.  
  1167.           case '?':
  1168.           case ctrl('G'):
  1169.         if(FOOTER_ROWS(ps_global) == 1 && km_popped == 0){
  1170.         km_popped++;
  1171.         FOOTER_ROWS(ps_global) = 3;
  1172.         line = -3;
  1173.         real_line = ps_global->ttyo->screen_rows + line;
  1174.         clearfooter(ps_global);
  1175.         draw_radio_prompt(real_line, RAD_BUT_COL, q);
  1176.         break;
  1177.         }
  1178.  
  1179.         if(help_text != NO_HELP && FOOTER_ROWS(ps_global) > 1){
  1180.         mark_keymenu_dirty();
  1181.         MoveCursor(real_line + 1, RAD_BUT_COL);
  1182.         CleartoEOLN();
  1183.         MoveCursor(real_line + 2, RAD_BUT_COL);
  1184.         CleartoEOLN();
  1185.         radio_help(real_line, RAD_BUT_COL, help_text);
  1186.         sleep(5);
  1187.         MoveCursor(real_line, RAD_BUT_COL + strlen(q));
  1188.         }
  1189.         else
  1190.           Writechar(BELL, 0);
  1191.  
  1192.             break;
  1193.             
  1194.  
  1195.           case NO_OP_COMMAND:
  1196.         goto newcmd;        /* misunderstood escape? */
  1197.         break;            /* never reached */
  1198.  
  1199.           case NO_OP_IDLE:        /* UNODIR, keep the stream alive */
  1200.         if(flags & RB_NO_NEWMAIL)
  1201.           goto newcmd;
  1202.         else if(new_mail(0, 2, 0) < 0)
  1203.               break;            /* no changes, get on with life */
  1204.             /* Else fall into redraw to adjust displayed numbers and such */
  1205.  
  1206.  
  1207.           case KEY_RESIZE:
  1208.           case ctrl('L'):
  1209.             real_line = line > 0 ? line : ps_global->ttyo->screen_rows + line;
  1210.             EndInverse();
  1211.             ClearScreen();
  1212.             redraw_titlebar();
  1213.             if(ps_global->redrawer != NULL)
  1214.               (*ps_global->redrawer)();
  1215.             redraw_keymenu();
  1216.             draw_radio_prompt(real_line, RAD_BUT_COL, q);
  1217.             break;
  1218.  
  1219.         } /* switch */
  1220.     }
  1221.  
  1222.   out_of_loop:
  1223.     fs_give((void **)&q);
  1224.     if(ds)
  1225.       fs_give((void **)&ds);
  1226.  
  1227.     EndInverse();
  1228.     fflush(stdout);
  1229.     resume_busy_alarm();
  1230.     if(km_popped){
  1231.     FOOTER_ROWS(ps_global) = 1;
  1232.     clearfooter(ps_global);
  1233.     ps_global->mangled_body = 1;
  1234.     }
  1235.  
  1236.     return(ch);
  1237. }
  1238.  
  1239.  
  1240. /*----------------------------------------------------------------------
  1241.  
  1242.   ----*/
  1243. void
  1244. radio_help(line, column, help)
  1245.      int line, column;
  1246.      HelpType help;
  1247. {
  1248.     char **text;
  1249.  
  1250. #if defined(HELPFILE)
  1251.     text = get_help_text(help, NULL);
  1252. #else
  1253.     text = help;
  1254. #endif
  1255.     if(text == NULL)
  1256.       return;
  1257.     
  1258.     EndInverse();
  1259.     MoveCursor(line + 1, column);
  1260.     CleartoEOLN();
  1261.     if(text[0])
  1262.       PutLine0(line + 1, column, text[0]);
  1263.  
  1264.     MoveCursor(line + 2, column);
  1265.     CleartoEOLN();
  1266.     if(text[1])
  1267.       PutLine0(line + 2, column, text[1]);
  1268.  
  1269.     StartInverse();
  1270. #if defined(HELPFILE)
  1271.     free_help_text(text);
  1272. #endif
  1273.     fflush(stdout);
  1274. }
  1275.  
  1276.  
  1277. /*----------------------------------------------------------------------
  1278.    Paint the screen with the radio buttons prompt
  1279.   ----*/
  1280. void
  1281. draw_radio_prompt(l, c, q)
  1282.     int       l, c;
  1283.     char     *q;
  1284. {
  1285.     int x;
  1286.  
  1287.     StartInverse();
  1288.     PutLine0(l, c, q);
  1289.     x = c + strlen(q);
  1290.     MoveCursor(l, x);
  1291.     while(x++ < ps_global->ttyo->screen_cols)
  1292.       Writechar(' ', 0);
  1293.     MoveCursor(l, c + strlen(q));
  1294.     fflush(stdout);
  1295. }
  1296.